unit CPToken;

interface

uses SysUtils, CPParserBase;

type
  { Desribes the type of a token }
  TCPTokenKind = (
    { Token is whitespace }
    tkWhitespace,
    { Token is a variable }
    tkVariable,
    { Token is a built-in function }
    tkBuiltinFunction,
    { Token is a number literal }
    tkNumber,
    { Token is an operator }
    tkOperator,
    { Token is a sign }
    tkSign,
    { Token is a left parenthesis }
    tkLeftParen,
    { Token is a right parenthesis }
    tkRightParen,
    { Character that separates arguments in function calls ("," by default) }
    tkArgSeparator);

  { Set of tokens that may be requested by the tokenizer }
  TCPExpectedTokenKind = set of (etWhitespace, etOperator, etOperand,
    etLeftParen, etRightParen, etArgSeparator, etEnd);


const
  { Provides textural descriptions for all values of TCPTokenKind }
  CPTokenKindStrings: array[Low(TCPTokenKind)..High(TCPTokenKind)] of string =
    ('Whitespace',
     'Variable',
     'Function',
     'Number',
     'Operator',
     'Sign',
     'Left parenthesis',
     'Right parenthesis',
     'Argument separator');

type
  { Represents a character seqeunce with a certain syntactic meaning.
    This class serves as the base class for certain token types that
    contain additional information (e.g. a number token has numeric value) }
  TCPToken = class
  private
    { Text sequence that represents this token }
    FSequence: TCPTextSequence;
    { Kind of token (e.g. number, identifier, parenthesis, ...) }
    FKind: TCPTokenKind;
    { Paren level after this token. Each opening parenthesis increases the
      paren level by one and each closing parenthesis decreases it by 1.
      The paren level at the start of an expression is 0. }
    FParenLevel: Integer;
    procedure SetSequence(const Value: TCPTextSequence);
  public
    { Creates a new token instance
      @param Kind Kind of token that is created
      @param PaenLevel Paren level after this token }
    constructor Create(Kind: TCPTokenKind; ParenLevel: Integer); virtual;
    destructor Destroy; override;
    { Returns the token as string }
    function ToString: string;
      {$IF CompilerVersion < 20} virtual; {$ELSE} override; {$IFEND}
    { Returns a shallow copy of this token }
    function Clone: TCPToken; virtual;
    { Gets or sets the text sequence that represents this token }
    property Sequence: TCPTextSequence read FSequence write SetSequence;
    { Gets the kind of token }
    property Kind: TCPTokenKind read FKind write FKind;
    { Gets or sets the paren level }
    property ParenLevel: Integer read FParenLevel write FParenLevel;
  end;

  { Class type for all token classes }
  TCPTokenClass = class of TCPToken;

  { Describes an operator }
  TCPOperatorToken = class(TCPToken)
  private
    { Operator }
    FOperator: PCPOperator;
  public
    { Returns a shallow copy of this token }
    function Clone: TCPToken; override;
    { Gets or sets the type of operator }
    property Operator
      : PCPOperator read FOperator write FOperator;
  end;

  { Describes an identifier }
  TCPIdentifierToken = class(TCPToken)
  private
    function GetName: string;
  public
    property Name: string read GetName;
  end;

  { Describes a number }
  TCPNumberToken = class(TCPToken)
  private
    { Numeric value of the number }
    FValue: TCPFloat;
  public
    { Returns a shallow copy of this token }
    function Clone: TCPToken; override;
    { Gets or sets the numeric value }
    property Value: TCPFloat read FValue write FValue;
  end;

implementation

{ TCPToken }

constructor TCPToken.Create(Kind: TCPTokenKind; ParenLevel: Integer);
begin
  FSequence := nil;
  FKind := Kind;
  FParenLevel := ParenLevel;
end;

destructor TCPToken.Destroy;
begin
  FSequence.Free;
end;

procedure TCPToken.SetSequence(const Value: TCPTextSequence);
begin
  if FSequence <> Value then
  begin
    FSequence.Free;
    FSequence := Value;
  end;
end;

function TCPToken.ToString: string;
begin
  Result := Format('%s: "%s"', [CPTokenKindStrings[FKind], FSequence.ToString]);
end;

function TCPToken.Clone: TCPToken;
begin
  Result := TCPToken.Create(FKind, FParenLevel);
  Result.FSequence := TCPTextSequence.Create(FSequence);
end;

{ TCPNumberToken }

function TCPNumberToken.Clone: TCPToken;
begin
  Result := inherited Clone;
  TCPNumberToken(Result).FValue := FValue;
end;

{ TCPIdentifierToken }

function TCPIdentifierToken.GetName: string;
begin
  Result := Sequence.ToString;
end;

{ TCPOperatorToken }

function TCPOperatorToken.Clone: TCPToken;
begin
  Result := inherited Clone;
  TCPOperatorToken(Result).FOperator := FOperator;
end;

end.
